<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2024/4/2
 * Time: 11:40
 */

namespace plugin\admin\app\logic;


use plugin\admin\app\common\Auth;
use plugin\admin\app\common\Tree;
use plugin\admin\app\common\Util;
use plugin\admin\app\model\admin\AdminRole;
use plugin\admin\app\model\admin\Role;
use plugin\admin\app\model\novel\NovelChapterModel;
use plugin\admin\app\model\novel\NovelSpreadModel;
use plugin\admin\app\model\plan\PromotionPlanModel;
use plugin\admin\app\model\user\UserModel;
use support\Db;
use support\exception\BusinessException;
use support\Request;
use support\Response;

class BaseLogic
{
    /**
     * @var Model
     */
    protected $model = null;

    /**
     * 无需登录及鉴权的方法
     * @var array
     */
    protected $noNeedLogin = [];

    /**
     * 需要登录无需鉴权的方法
     * @var array
     */
    protected $noNeedAuth = [];

    /**
     * 数据限制
     * 例如当$dataLimit='personal'时将只返回当前管理员的数据
     * @var string
     */
    protected $dataLimit = null;

    /**
     * 数据限制字段
     */
    protected $dataLimitField = 'admin_id';


    /**
     * 查询
     * @param Request $request
     * @return Response
     * @throws BusinessException
     */
    public function select(Request $request): Response
    {
        [$where, $format, $limit, $field, $order] = $this->selectInput($request);
        $query = $this->doSelect($where, $field, $order);
        return $this->doFormat($query, $format, $limit);
    }

    /**
     * 添加
     * @param Request $request
     * @return Response
     * @throws BusinessException
     */
    public function insert(Request $request): Response
    {
        $data = $this->insertInput($request);
        $id = $this->doInsert($data);
        return $this->json(0, 'ok', ['id' => $id]);
    }

    /**
     * 更新
     * @param Request $request
     * @return Response
     * @throws BusinessException
     */
    public function update(Request $request): Response
    {
        [$id, $data] = $this->updateInput($request);
        $this->doUpdate($id, $data);
        return $this->json(0);
    }

    /**
     * 删除
     * @param Request $request
     * @return Response
     * @throws BusinessException
     */
    public function delete(Request $request): Response
    {
        $ids = $this->deleteInput($request);
        $this->doDelete($ids);
        return $this->json(0);
    }

    /**
     * 删除
     * @param Request $request
     * @return Response
     * @throws BusinessException
     */
    public function isDelete(Request $request): Response
    {
        $ids = $this->deleteInput($request);
        $this->doIsDelete($ids);
        return $this->json(0);
    }

    /**
     * 查询前置
     * @param Request $request
     * @return array
     * @throws BusinessException
     */
    protected function selectInput(Request $request): array
    {
        $field = $request->get('field');
        $order = $request->get('order', 'asc');
        $format = $request->get('format', 'normal');
        $limit = (int)$request->get('limit', $format === 'tree' ? 1000 : 10);
        $limit = $limit <= 0 ? 10 : $limit;
        $order = $order === 'asc' ? 'asc' : 'desc';
        $where = $request->get();
        $page = (int)$request->get('page');
        $page = $page > 0 ? $page : 1;
        $table = config('plugin.admin.database.connections.mysql.prefix') . $this->model->getTable();

        $allow_column = Util::db()->select("desc `$table`");
        if (!$allow_column) {
            throw new BusinessException('表不存在');
        }
        $allow_column = array_column($allow_column, 'Field', 'Field');
        if (!in_array($field, $allow_column)) {
            $field = null;
        }
        foreach ($where as $column => $value) {
            if (
                $value === '' || !isset($allow_column[$column]) ||
                is_array($value) && (empty($value) || (empty($value[0]) && empty($value[1])) || !in_array($value[0], ['null', 'not null']) && !isset($value[1]))
            ) {
                unset($where[$column]);
            }
        }
        // 按照数据限制字段返回数据
        if ($this->dataLimit === 'personal') {
            $where[$this->dataLimitField] = admin_id();
        } elseif ($this->dataLimit === 'auth') {
            $primary_key = $this->model->getKeyName();
            if (!Auth::isSupperAdmin() && (!isset($where[$primary_key]) || $this->dataLimitField != $primary_key)) {
                $where[$this->dataLimitField] = ['in', Auth::getScopeAdminIds(true)];
            }
        }
        return [$where, $format, $limit, $field, $order, $page];
    }

    /**
     * 指定查询where条件,并没有真正的查询数据库操作
     * @param array $where
     * @param string|null $field
     * @param string $order
     * @return EloquentBuilder|QueryBuilder|Model
     */
    protected function doSelect(array $where, string $field = null, string $order= 'desc')
    {
        $model = $this->model;
        foreach ($where as $column => $value) {
            if (is_array($value)) {
                if ((($value[0] === 'like' || $value[0] === 'not like') && !empty($value[1]))) {
                    $model = $model->where($column, $value[0], "%$value[1]%");
                } elseif (in_array($value[0], ['>','>=', '=', '<=', '<', '<>'])) {
                    $model = $model->where($column, $value[0], $value[1]);
                } elseif ($value[0] == 'in' && !empty($value[1])) {
                    $valArr = $value[1];
                    if (is_string($value[1])) {
                        $valArr = explode(",", trim($value[1]));
                    }
                    $model = $model->whereIn($column, $valArr);
                } elseif ($value[0] == 'not in' && !empty($value[1])) {
                    $valArr = $value[1];
                    if (is_string($value[1])) {
                        $valArr = explode(",", trim($value[1]));
                    }
                    $model = $model->whereNotIn($column, $valArr);
                }elseif ($value[0] == 'null') {
                    $model = $model->whereNull($column);
                } elseif ($value[0] == 'not null') {
                    $model = $model->whereNotNull($column);
                } elseif ($value[0] == 'find' && !empty($value[1])) {
                    $model = $model->whereRaw("FIND_IN_SET('{$value[1]}',$column)>0");
                } elseif (preg_match('/^\d+/',$value[0]) && preg_match('/^\d+/',$value[1])) {
                    $model = $model->whereBetween($column, $value);
                }
            } else {
                if(strpos($value,'~')){
                    $valArr = explode('~',str_replace([' '],[''],$value));
                    if(preg_match('/\d{4}\-\d{2}\-\d{2}/',$valArr[0]) && preg_match('/\d{4}\-\d{2}\-\d{2}/',$valArr[1])){
                        $valArr[0] .= ' 00:00:00';
                        $valArr[1] .= ' 23:59:59';
                        $model = $model->whereBetween($column, $valArr);
                    }
                }else{
                    $model = $model->where($column, $value);
                }
            }
        }
        if ($field) {
            $model = $model->orderBy($field, $order);
        }
        return $model;
    }

    /**
     * 执行真正查询，并返回格式化数据
     * @param $query
     * @param $format
     * @param $limit
     * @return Response
     */
    protected function doFormat($query, $format, $limit): Response
    {
        $methods = [
            'select' => 'formatSelect',
            'tree' => 'formatTree',
            'table_tree' => 'formatTableTree',
            'normal' => 'formatNormal',
        ];
        $paginator = $query->paginate($limit);
        $total = $paginator->total();
        $items = $paginator->items();
        if (method_exists($this, "afterQuery")) {
            $items = call_user_func([$this, "afterQuery"], $items);
        }
        $format_function = $methods[$format] ?? 'formatNormal';
        return call_user_func([$this, $format_function], $items, $total);
    }

    /**
     * 插入前置方法
     * @param Request $request
     * @return array
     * @throws BusinessException
     */
    protected function insertInput(Request $request): array
    {
        $data = $this->inputFilter($request->post());
        $password_filed = 'password';
        if (isset($data[$password_filed])) {
            $data[$password_filed] = Util::passwordHash($data[$password_filed]);
        }

        if (!Auth::isSupperAdmin() && $this->dataLimit) {
            if (!empty($data[$this->dataLimitField])) {
                $admin_id = $data[$this->dataLimitField];
                if (!in_array($admin_id, Auth::getScopeAdminIds(true))) {
                    throw new BusinessException('无数据权限');
                }
            }
        }
        return $data;
    }

    /**
     * 执行插入
     * @param array $data
     * @return mixed|null
     */
    protected function doInsert(array $data)
    {
        $primary_key = $this->model->getKeyName();
        $model_class = get_class($this->model);
        $model = new $model_class;
        foreach ($data as $key => $val) {
            $model->{$key} = $val;
        }
        $model->save();
        return $primary_key ? $model->$primary_key : null;
    }

    /**
     * 获取编号
     * @param $snKey
     * @param string $fix
     * @param int $len
     * @param int $randLen
     * @return string
     */
    protected function getSn($snKey,$fix='',$len=4,$randLen=0)
    {
        $maxId =  $this->model->max($snKey);
        $sn = Util::getSnCode($maxId,$fix,$len, $randLen);

        return $sn;
    }

    /**
     * 更新前置方法
     * @param Request $request
     * @return array
     * @throws BusinessException
     */
    protected function updateInput(Request $request): array
    {
        $primary_key = $this->model->getKeyName();
        $id = $request->post($primary_key);
        $data = $this->inputFilter($request->post());
        $model = $this->model->find($id);
        if (!$model) {
            throw new BusinessException('记录不存在', 2);
        }
        if (!Auth::isSupperAdmin() && $this->dataLimit) {
            $scopeAdminIds = Auth::getScopeAdminIds(true);
            $admin_ids = [
                $data[$this->dataLimitField] ?? false, // 检查要更新的数据admin_id是否是有权限的值
                $model->{$this->dataLimitField} ?? false // 检查要更新的记录的admin_id是否有权限
            ];
            foreach ($admin_ids as $admin_id) {
                if ($admin_id && !in_array($admin_id, $scopeAdminIds)) {
                    throw new BusinessException('无数据权限');
                }
            }
        }
        $password_filed = 'password';
        if (isset($data[$password_filed])) {
            // 密码为空，则不更新密码
            if ($data[$password_filed] === '') {
                unset($data[$password_filed]);
            } else {
                $data[$password_filed] = Util::passwordHash($data[$password_filed]);
            }
        }
        unset($data[$primary_key]);
        return [$id, $data];
    }

    /**
     * 执行更新
     * @param $id
     * @param $data
     * @return void
     */
    protected function doUpdate($id, $data)
    {
        $model = $this->model->find($id);
        foreach ($data as $key => $val) {
            $model->{$key} = $val;
        }
        $model->save();
    }

    /**
     * 对用户输入表单过滤
     * @param array $data
     * @return array
     * @throws BusinessException
     */
    protected function inputFilter(array $data): array
    {
        $table = config('plugin.admin.database.connections.mysql.prefix') . $this->model->getTable();
        $allow_column = $this->model->getConnection()->select("desc `$table`");
        if (!$allow_column) {
            throw new BusinessException('表不存在', 2);
        }
        $columns = array_column($allow_column, 'Type', 'Field');
        foreach ($data as $col => $item) {
            if (!isset($columns[$col])) {
                unset($data[$col]);
                continue;
            }
            // 非字符串类型传空则为null
            if ($item === '' && strpos(strtolower($columns[$col]), 'varchar') === false && strpos(strtolower($columns[$col]), 'text') === false) {
                //$data[$col] = null;
            }
            if (is_array($item)) {
                $data[$col] = implode(',', $item);
            }
        }
        if (empty($data['created_at'])) {
            unset($data['created_at']);
        }
        if (empty($data['updated_at'])) {
            unset($data['updated_at']);
        }
        return $data;
    }

    /**
     * 删除前置方法
     * @param Request $request
     * @return array
     * @throws BusinessException
     */
    protected function deleteInput(Request $request): array
    {
        $primary_key = $this->model->getKeyName();
        if (!$primary_key) {
            throw new BusinessException('该表无主键，不支持删除');
        }
        $ids = (array)$request->post($primary_key, []);
        if (!Auth::isSupperAdmin() && $this->dataLimit) {
            $admin_ids = $this->model->where($primary_key, $ids)->pluck($this->dataLimitField)->toArray();
            if (array_diff($admin_ids, Auth::getScopeAdminIds(true))) {
                throw new BusinessException('无数据权限');
            }
        }
        return $ids;
    }

    /**
     * 执行删除-物理删除
     * @param array $ids
     * @return void
     */
    protected function doDelete(array $ids)
    {
        if (!$ids) {
            return;
        }
        $primary_key = $this->model->getKeyName();
        $this->model->whereIn($primary_key, $ids)->delete();
    }

    /**
     * 执行删除-软删除
     * @param array $ids
     * @return void
     */
    protected function doIsDelete(array $ids)
    {
        if (!$ids) {
            return;
        }
        $primary_key = $this->model->getKeyName();
        $this->model->whereIn($primary_key, $ids)->update(['isdel'=>time()]);
    }

    /**
     * 格式化树
     * @param $items
     * @return Response
     */
    protected function formatTree($items): Response
    {
        $format_items = [];
        foreach ($items as $item) {
            $format_items[] = [
                'name' => $item->title ?? $item->name ?? $item->id,
                'value' => (string)$item->id,
                'id' => $item->id,
                'pid' => $item->pid,
            ];
        }
        $tree = new Tree($format_items);
        return json(['code' => 0, 'msg' => 'ok', 'data' => $tree->getTree()]);
    }

    /**
     * 格式化表格树
     * @param $items
     * @return Response
     */
    protected function formatTableTree($items): Response
    {
        $tree = new Tree($items);
        return json(['code' => 0, 'msg' => 'ok', 'data' => $tree->getTree()]);
    }

    /**
     * 格式化下拉列表
     * @param $items
     * @return Response
     */
    protected function formatSelect($items): Response
    {
        $formatted_items = [];
        foreach ($items as $item) {
            $formatted_items[] = [
                'name' => $item->title ?? $item->name ?? $item->plan_name ?? $item->id,
                'value' => $item->id
            ];
        }
        return  json(['code' => 0, 'msg' => 'ok', 'data' => $formatted_items]);
    }

    /**
     * 通用格式化
     * @param $items
     * @param $total
     * @return Response
     */
    protected function formatNormal($items, $total): Response
    {
        return json(['code' => 0, 'msg' => 'ok', 'count' => $total, 'data' => $items]);
    }

    /**
     * 查询数据库后置方法，可用于修改数据
     * @param mixed $items 原数据
     * @return mixed 修改后数据
     */
    protected function afterQuery($items)
    {
        return $items;
    }

    protected function checkDatabaseAvailable()
    {
        if (!config('plugin.admin.database')) {
            throw new BusinessException('请重启webman');
        }
    }

    protected function loginData($user)
    {
        unset($user->userid);

        $user->login_time = time();

        $expireTime = 1000 * 86400;

        $user->expire_time = time() + $expireTime;

        $user->token = JwtToken::getInstance()->createToken($user->toArray(), $expireTime);


        $createtime = strtotime($user->createtime);
        $this->unsetCommonFields($user,['source_ip','expire_time','login_time','uuid','scene']);

        //登录记录
        UserLoginRecordModel::getInstance()->insertData($user);

        return $user;
    }

    /**
     * 移除不包含某些数据的数组
     * @param $array
     * @param $key
     * @param $values
     * @return void
     */
    protected function removeNotContain(&$array, $key, $values)
    {
        foreach ($array as $k => &$item) {
            if (!is_array($item)) {
                continue;
            }
            if (!$this->arrayContain($item, $key, $values)) {
                unset($array[$k]);
            } else {
                if (!isset($item['children'])) {
                    continue;
                }
                $this->removeNotContain($item['children'], $key, $values);
            }
        }
    }

    /**
     * 判断数组是否包含某些数据
     * @param $array
     * @param $key
     * @param $values
     * @return bool
     */
    protected function arrayContain(&$array, $key, $values): bool
    {
        if (!is_array($array)) {
            return false;
        }
        if (isset($array[$key]) && in_array($array[$key], $values)) {
            return true;
        }
        if (!isset($array['children'])) {
            return false;
        }
        foreach ($array['children'] as $item) {
            if ($this->arrayContain($item, $key, $values)) {
                return true;
            }
        }
        return false;
    }

    protected function emptyFilter($menus)
    {
        return array_map(
            function ($menu) {
                if (isset($menu['children'])) {
                    $menu['children'] = $this->emptyFilter($menu['children']);
                }
                return $menu;
            },
            array_values(array_filter(
                $menus,
                function ($menu) {
                    return $menu['type'] != 0 || isset($menu['children']) && count($this->emptyFilter($menu['children'])) > 0;
                }
            ))
        );
    }

    /**
     * 是否是超级管理员
     * @param int $admin_id
     * @return bool
     */
    public function isSupperAdmin(int $admin_id = 0): bool
    {
        if (!$admin_id) {
            if (!$roles = request()->curAdminInfo['role_ids']) {
                return false;
            }
        } else {
            $roles = AdminRole::where('admin_id', $admin_id)->pluck('role_id');
        }
        $rules = Role::whereIn('id', $roles)->pluck('rules');
        return $rules && in_array('*', $rules->toArray());
    }

    /**
     * 返回格式化json数据
     *
     * @param int $code
     * @param string $msg
     * @param array $data
     * @return Response
     */
    protected function json(int $code, string $msg = 'ok', array $data = []): Response
    {
        return json(['code' => $code, 'data' => $data, 'msg' => $msg]);
    }

    /**
     * 跟数据列表对应id获取关系表数据---一对一 key映射模式
     * @param $model
     * @param $data
     * @param $keyIdField
     * @param $idField
     * @param array $extParam['field','where']
     * @return array
     * @throws \think\Exception
     */
    public function getListMapById($model,$data,$keyIdField,$idField,$extParam=[])
    {
        if(empty($data) || empty($model)){
            return [];
        }

        $ids = array_column($data, $keyIdField);
        $ids && $ids =  array_unique($ids);

        $fields = $extParam['field']??'*';
        $extWhere = $extParam['where']??[];

        if($fields!='*'){
            if(is_string($fields)){
                $fields = explode(',', $fields);
            }
            array_unshift($fields,$idField);
            $fields = array_unique($fields);
        }

        $listData = $model->getWhere($extWhere)->whereIn($idField,$ids)->select($fields)->get()->toArray();

        if($listData){
            return array_column($listData,null,$idField);
        }

        return [];
    }

    /**
     * 跟数据列表对应id获取关系表数据---一对多模式
     * @param $model
     * @param $data
     * @param $keyIdField
     * @param $idField
     * @param array $extParam['field','where','order'=>['sort desc','id asc']]
     * @return array
     * @throws \think\Exception
     */
    public function getListAllMapById($model,$data,$keyIdField,$idField,$extParam=[])
    {
        if(empty($data) || empty($model)){
            return [];
        }

        $ids = array_column($data, $keyIdField);
        $ids && $ids =  array_unique($ids);

        $fields = $extParam['field']??'*';
        $extWhere = $extParam['where']??[];
        $orders = $extParam['order']??[];

        if($fields!='*'){
            if(is_string($fields)){
                $fields = explode(',', $fields);
            }
            array_unshift($fields,$idField);
            $fields = array_unique($fields);
        }

        $query = $model->getWhere($extWhere)->whereIn($idField,$ids);
        if($orders){
            foreach($orders as $order){
                $orderArr = explode(' ',$order);
                $query = $query->orderBy($orderArr[0],$orderArr[1]);
            }
        }
        $listData = $query->select($fields)->get()->toArray();

        $returnData = [];
        if($listData){
            foreach ($listData as $val){
                $returnData[$val[$idField]][] = $val;
            }
        }

        return $returnData;
    }

    /**
     * 跟数据列表对应id获取关系表数据---多对多模式
     * @param $model
     * @param $relatedModel
     * @param $data
     * @param $keyIdField
     * @param $idField
     * @param array $extParam['field','where','order'=>['sort desc','id asc'],'relatedKey'=>['keyIdField','idField']
     * @return array
     * @throws \think\Exception
     */
    public function getListAllMapDataById($model,$relatedModel,$data,$keyIdField,$idField,$extParam=[])
    {
        if(empty($data) || empty($model) || empty($relatedModel)){
            return [];
        }
        $keyIdFieldKey = $keyIdField;
        $idFieldKey = $idField;
        if(!empty($extParam['relatedKey'])){
            $keyIdFieldKey = $extParam['relatedKey'][0];
            $idFieldKey = $extParam['relatedKey'][1];
        }


        $ids = array_column($data, $keyIdField);
        $ids && $ids =  array_unique($ids);

        $fields = $extParam['field']??'*';
        $extWhere = $extParam['where']??[];
        $orders = $extParam['order']??[];

        if($fields!='*'){
            if(is_string($fields)){
                $fields = explode(',', $fields);
            }
            array_unshift($fields,'b.'.$idField);
            array_unshift($fields,'b.'.$keyIdFieldKey);
            $fields = array_unique($fields);
        }

        $query = $model->setTable($model->getTable().' as a')
            ->join($relatedModel->getTable().' as b','a.'.$idFieldKey,'=','b.'.$idField)
            ->where($extWhere)->whereIn($keyIdFieldKey,$ids);

        if($orders){
            foreach($orders as $order){
                $orderArr = explode(' ',$order);
                $query = $query->orderBy($orderArr[0],$orderArr[1]);
            }
        }
        $listData = $query->select($fields)->get()->toArray();

        $returnData = [];
        if($listData){
            foreach ($listData as $val){
                $returnData[$val[$keyIdField]][] = $val;
            }
        }

        return $returnData;
    }

    /**
     * 跟数据对应id获取关系表数据,获取详情数据
     * @param $model
     * @param $data
     * @param $keyIdField
     * @param $idField
     * @param array $extParam
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function getDetailDataById($model,$data,$keyIdField,$idField,$extParam=[])
    {
        if(empty($data) || !isset($data[$keyIdField])){
            return [];
        }

        $id = $data[$keyIdField];

        $fields = $extParam['field']??'*';
        $extWhere = $extParam['where']??[];

        if($fields!='*'){
            if(is_string($fields)){
                $fields = explode(',', $fields);
            }
            array_unshift($fields,$idField);
            $fields = array_unique($fields);
        }

        $detailData =  $model->getWhere($extWhere)->where($idField,$id)->select($fields)->first();

        if($detailData){
            return $detailData->toArray();
        }

        return [];
    }

    /**
     * 更新方案数量
     * @return bool
     */
    protected function updatePlanNums()
    {
//        $fields = [Db::Raw('count(chapterid) counts'),'chapterid'];
//        $chapterData = PromotionPlanModel::getInstance()->getWhere(['isdel'=>0])->groupBy('chapterid')->get($fields);
//        foreach ($chapterData as $val){
//            NovelChapterModel::getInstance()->getWhere(['id'=>$val['chapterid']])->update(['plan_nums'=>$val['counts']]);
//        }
//
//        $fields = [Db::Raw('count(novel_sn) counts'),'novel_sn'];
//        $novelData = PromotionPlanModel::getInstance()->getWhere(['isdel'=>0])->groupBy('novel_sn')->get($fields);
//        foreach ($novelData as $val){
//            NovelSpreadModel::getInstance()->getWhere(['novel_sn'=>$val['novel_sn']])->update(['plan_nums'=>$val['counts']]);
//        }

        return true;
    }

    protected function checkIsVip($user)
    {
        $isVip = $user['is_vip'];
        $expireTime = 0;
        !empty($user['vip_expire_time']) && $expireTime = strtotime($user['vip_expire_time']);
        if($user['is_vip']>0 && $expireTime>0 && ($expireTime-time())>(86400*365*10)){
            //永久会员
            $user['is_vip']=$isVip>0 ? $isVip : 1;
        }elseif($user['is_vip']>0 && $expireTime>0 && $expireTime>time()){
            $user['is_vip']=$isVip>0 ? $isVip : 1;
        }else{
            $user['is_vip']=0;
        }

        return $user;
    }
}